//
// Created by Kai Wang on 23-11-21.
//

#include "order/order.h"
#include "order/order_comment.h"
#include <fmt/format.h>
#include <iomanip>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

Order::Order(
    int                    serial_number,
    time_point             create_time,
    time_point             finish_time,
    time_point             cancel_time,
    int                    customer_id,
    std::vector<OrderItem> ordered_items,
    float                  total_price,
    // OrderComment           order_comment,
    OrderStatus order_status
)
{
    serial_number_ = serial_number;
    create_time_   = create_time;
    finish_time_   = finish_time;
    cancel_time_   = cancel_time;
    customer_id_   = customer_id;
    ordered_items_ = std::move(ordered_items);
    total_price_   = total_price;
    // order_comment_ = order_comment;
    status_ = order_status;
}

float OrderItem::get_price() const
{
    return this->first.get_price() * static_cast<float>(this->second);
}

std::string OrderItem::get_info() const
{
    return fmt::format("{} x {}", this->first.get_name(), this->second);
}

int Order::get_serial_number() const
{
    return serial_number_;
}

int Order::get_customer_id() const
{
    return customer_id_;
}

float Order::get_total_price() const
{
    return total_price_;
}

void Order::set_customer(const Customer &customer)
{
    customer_    = customer;
    customer_id_ = customer.get_id();
}

void Order::set_status(OrderStatus status)
{
    status_ = status;
}

OrderStatus Order::get_status() const
{
    return status_;
}

const time_point &Order::get_create_time() const
{
    return create_time_;
}

const time_point &Order::get_finish_time() const
{
    return finish_time_;
}

const time_point &Order::get_cancel_time() const
{
    return cancel_time_;
}

void Order::set_create_time(const time_point &create_time)
{
    create_time_ = create_time;
}

void Order::set_finish_time(const time_point &finish_time)
{
    finish_time_ = finish_time;
}

void Order::set_cancel_time(const time_point &cancel_time)
{
    cancel_time_ = cancel_time;
}

std::vector<OrderItem> &Order::get_ordered_items()
{
    return ordered_items_;
}

void Order::calc_total_price()
{
    float total_price = 0;
    for(const auto &order_item : ordered_items_)
    {
        total_price += order_item.get_price();
    }
    original_total_price_ = total_price;
    total_price_          = total_price * get_discount(customer_.get_member_rank());
}

float Order::get_original_total_price() const
{
    return original_total_price_;
}

Customer Order::get_customer() const
{
    return customer_;
}

OrderComment &Order::get_order_comment()
{
    return order_comment_;
}

namespace Convert
{
void TimePointToString(const time_point &tp, std::string &str)
{
    auto tp_c = std::chrono::system_clock::to_time_t(tp);
    str       = std::ctime(&tp_c);
}

void StringToTimePoint(const std::string &str, time_point &tp)
{
    std::tm           tm {};
    std::stringstream ss(str);
    ss >> std::get_time(&tm, "%a %b %d %H:%M:%S %Y");
    tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
}

std::string TimePointToString(const time_point &tp)
{
    auto tp_c = std::chrono::system_clock::to_time_t(tp);
    auto str  = std::ctime(&tp_c);
    return str;
}

void OrderItemToString(const OrderItem &order_item, std::string &str)
{
    auto &[item, num] = order_item;
    str               = fmt::format("{}*{}", item.get_name(), num);
}

std::string OrderItemsToString(const std::vector<OrderItem> &order_items)
{
    std::string str_all;
    for(const auto &order_item : order_items)
    {
        std::string str;
        OrderItemToString(order_item, str);
        str_all.append(" ").append(str);
    }
    return str_all;
}

void StringToOrderItem(const std::string &str, OrderItem &order_item)
{
    std::string name;
    int         num;
    // split by *
    auto pos = str.find('*');
    if(pos == std::string::npos)
    {
        throw std::invalid_argument("invalid string to convert to OrderItem");
    }
    name = str.substr(0, pos);
    num  = std::stoi(str.substr(pos + 1));
    // init item
    order_item.first  = Item(name);
    order_item.second = num;
}

void StringToOrderItems(const std::string &str, std::vector<OrderItem> &order_items)
{
    std::stringstream ss;
    ss << str;
    auto doc = rapidcsv::Document(
        ss, rapidcsv::LabelParams(), rapidcsv::SeparatorParams(' '), rapidcsv::ConverterParams(" ")
    );
    auto order_item_str_list = doc.GetColumnNames();
    for(const auto &order_item_str : order_item_str_list)
    {
        OrderItem order_item;
        StringToOrderItem(order_item_str, order_item);
        order_items.push_back(order_item);
    }
}

}  // namespace Convert
